using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Web;
using System.Data;
using Newtonsoft.Json;

namespace Models
{
    public enum PropertyErrorType { Required, Integrity }

    public abstract class BaseObject
    {
        public virtual Dictionary<string, string> ToDictionary()
        {
            return ObjectToDictionary(this);
        }

        public virtual Dictionary<string, object> ToDictionaryObj()
        {
            return ObjectToDictionaryObj(this);
        }

        public virtual Dictionary<string, PropertyErrorType> CheckData(bool isBeforeSave)
        {
            return new Dictionary<string, PropertyErrorType>();
        }

        public virtual Dictionary<string, string> FindDifference(BaseObject objOld)
        {
            if ( objOld == null)
                throw new Exception("KpmgObject.FindDifference::Invalid parameter");

            Dictionary<string, string> dicNew = this.ToDictionary();
            Dictionary<string, string> dicOld = objOld.ToDictionary();

            Dictionary<string, string> dicRet = new Dictionary<string, string>();
            foreach (KeyValuePair<string, string> kv in dicOld)
            {
                string strOld = kv.Value;
                string strNew = dicNew.ContainsKey(kv.Key) ? dicNew[kv.Key] : "";
                if (strOld != strNew)
                    dicRet.Add(kv.Key, strOld + "->" + strNew);
            }
            return dicRet;
        }

        public override string ToString()
        {
            return JsonConvert.SerializeObject(this);
            //return DictionaryToString(ToDictionary());

        }

        public BaseObject() { }

        #region protected utility
        protected void CheckPropertyForRequired(ref Dictionary<string, PropertyErrorType> dicRet,
            string propName, string propValue)
        {
            if (String.IsNullOrEmpty(propValue))
                dicRet.Add(propName, PropertyErrorType.Required);
        }

        protected void CheckPropertyForRequired(ref Dictionary<string, PropertyErrorType> dicRet,
            string propName, DateTime propValue)
        {
            if (propValue == DateTime.MinValue)
                dicRet.Add(propName, PropertyErrorType.Required);
        }
        #endregion

        #region static utility methods
        public static string BoolToString(bool val)
        {
            return val ? "Y" : "N";
        }

        public static string DateToString(DateTime dt)
        {
            return dt == DateTime.MinValue ? "" : dt.ToString("yyyy-MM-dd HH:mm:ss");
        }

        public static string DateTimeToString(DateTime dt)
        {
            return dt == DateTime.MinValue ? "" : dt.ToString("yyyy-MM-dd HH:mm:ss");
        }

        public static string DictionaryToString(Dictionary<string, string> dic)
        {
            StringBuilder sb = new StringBuilder();

            foreach (KeyValuePair<string, string> kv in dic)
            {
                bool isComplex = kv.Value.IndexOf(",") >= 0 || kv.Value.IndexOf(":") >= 0;
                sb.Append(",")
                    .Append(kv.Key)
                    .Append(":")
                    .Append(isComplex ? "[" : String.Empty)
                    .Append(kv.Value)
                    .Append(isComplex ? "]" : String.Empty);
            }

            if (sb.Length > 0)  //Remove the first ","
                sb.Remove(0, 1);

            return sb.ToString();
        }

        public static string ListToString(List<string> lst)
        {
            StringBuilder sb = new StringBuilder();

            foreach (string str in lst)
            {
                bool isComplex = str.IndexOf(",") >= 0 || str.IndexOf(":") >= 0;
                sb.Append(",")
                    .Append(isComplex ? "[" : String.Empty)
                    .Append(str)
                    .Append(isComplex ? "]" : String.Empty);
            }

            if (sb.Length > 0)  //Remove the first ","
                sb.Remove(0, 1);

            return sb.ToString();
        }

        public static string ObjectToString(Object obj)
        {
            return DictionaryToString(ObjectToDictionary(obj));
        }

        public static Dictionary<string, object> ObjectToDictionaryObj(Object obj)
        {
            Dictionary<string, object> dicRet = new Dictionary<string, object>();

            //Auto-export basic properties
            PropertyInfo[] props = obj.GetType().GetProperties();
            foreach (PropertyInfo prop in props)
            {
                object propValue = prop.GetValue(obj, null);
                if (propValue != null)
                {
                    dicRet.Add(prop.Name, propValue);
                }
            }

            //Return
            return dicRet;
        
        }

        public static Dictionary<string, string> ObjectToDictionary(Object obj)
        {
            Dictionary<string, string> dicRet = new Dictionary<string, string>();

            //Auto-export basic properties
            PropertyInfo[] props = obj.GetType().GetProperties();
            foreach (PropertyInfo prop in props)
            {
                Type propType = prop.PropertyType;
                object propValue = prop.GetValue(obj, null);

                if (propValue != null)
                {
                    if (propType.Name == typeof(DateTime).Name)
                        dicRet.Add(prop.Name, DateToString((DateTime)propValue));
                    else if (propType.Name == typeof(bool).Name)
                        dicRet.Add(prop.Name, BoolToString((bool)propValue));
                    else if (propType.Name == typeof(string).Name || propType.Name == typeof(int).Name
                        || propType.Name == typeof(double).Name || propType.IsEnum)
                        dicRet.Add(prop.Name, propValue.ToString());
                }
            }

            //Return
            return dicRet;
        }

        public static List<string> StringToList(string str, char charSep, bool keepEmpty)
        {
            string[] arrStr = new string[0];
            if (keepEmpty)
                arrStr = str.Split(new char[] { charSep });
            else
                arrStr = str.Split(new char[] { charSep }, StringSplitOptions.RemoveEmptyEntries);

            return new List<string>(arrStr);
        }

        public static List<string> StringToList(string str, string sep, bool keepEmpty)
        {
            string[] arrStr = new string[0];
            if (keepEmpty)
                arrStr = str.Split(new string[] { sep }, StringSplitOptions.None);
            else
                arrStr = str.Split(new string[] { sep }, StringSplitOptions.RemoveEmptyEntries);

            return new List<string>(arrStr);
        }

        public static DateTime StringToDate(string str)
        {
            DateTime dtRet = DateTime.MinValue;

            DateTime.TryParseExact(str, "yyyy-MM-dd HH:mm:ss", null, System.Globalization.DateTimeStyles.None, out dtRet);

            return dtRet;
        }

        public static Dictionary<string, string> FindDifference<T>(IList<T> newObjs, IList<T> oldObjs)  where T : BaseObject
        {
            Dictionary<string, string> dicOld = new Dictionary<string, string>();
            int i = 0;
            foreach(BaseObject old in oldObjs)
            {
                i++;
                dicOld.Add(i.ToString(), old.ToString());
            }

            Dictionary<string, string> dicNew = new Dictionary<string, string>();
            i = 0;
            foreach (BaseObject newObj in newObjs)
            {
                i++;
                dicNew.Add(i.ToString(), newObj.ToString());
            }

            Dictionary<string, string> dicRet = new Dictionary<string, string>();
            foreach (KeyValuePair<string, string> kv in dicOld)
            {
                string strOld = kv.Value;
                string strNew = dicNew.ContainsKey(kv.Key) ? dicNew[kv.Key] : "";
                if (strOld != strNew)
                    dicRet.Add(kv.Key, "[" + strOld + "]->[" + strNew + "]");
            }

            foreach (KeyValuePair<string, string> kv in dicNew)
            {
                if (!dicOld.ContainsKey(kv.Key))
                {
                    string strNew = dicNew.ContainsKey(kv.Key) ? (string.IsNullOrEmpty(dicNew[kv.Key]) ? "" : dicNew[kv.Key]) : "";
                    if (!string.IsNullOrEmpty(strNew))
                    {
                        dicRet.Add(kv.Key, "[" + "]->[" + strNew + "]");
                    }
                }
            }

            return dicRet;
        }

        public static DataTable ListToTable(IList<object> lstData, IList<string> lstProp)
        {
            DataTable dt = new DataTable();

            #region Add columns
            foreach (string strProp in lstProp)
                dt.Columns.Add(new DataColumn(strProp, Type.GetType("System.String")));
            #endregion

            #region Add data rows
            if (lstData == null || lstData.Count == 0)
                return dt;

            //Get the list of PropertyInfo to extract value
            IList<PropertyInfo> lstPI = new List<PropertyInfo>();
            PropertyInfo[] props = lstData[0].GetType().GetProperties();
            foreach (string strProp in lstProp)
            {
                PropertyInfo objPI = props.Where(x => x.Name == strProp).FirstOrDefault();
                lstPI.Add(objPI);
            }

            foreach (object objData in lstData)
            {
                IList<string> lstValue = new List<string>();

                foreach (PropertyInfo objPI in lstPI)
                {
                    string strValue = String.Empty;
                   
                    if (objPI == null)
                    {
                        lstValue.Add(strValue);
                        continue;

                    }
                    Type propType = objPI.PropertyType;
                    object propValue = objPI.GetValue(objData, null);

                    if (propValue != null)
                    {
                        if (propType.Name == typeof(DateTime).Name)
                            strValue = BaseObject.DateTimeToString((DateTime)propValue);
                        else if (propType.Name == typeof(bool).Name)
                            strValue = BaseObject.BoolToString((bool)propValue);
                        else if (propType.Name == typeof(string).Name || propType.Name == typeof(int).Name
                            || propType.Name == typeof(double).Name || propType.IsEnum)
                            strValue = propValue.ToString();
                        else
                            strValue = "NA";
                    }
                    lstValue.Add(strValue);
                }

                dt.Rows.Add(lstValue.ToArray());
            }
            #endregion

            return dt;
        }
        #endregion
    }
}

